<script lang="ts" setup>
import { MessageTransaction, MessageTreeData, MessageTag, Message, MessageValue, TreeMenuItem, DialogApi, MessageValueToEdit, MessageTransactionData, ReceiveAction, CopiedData } from '../../common/types';
import { inject, nextTick, reactive, shallowRef } from 'vue';
import { ConnectionStatus, DialogResult, MessageTreeDataType, SecsFormat, SyncTransactionAction } from '../../common/enums';
import TreeDataTypeSvgVue from '../../components/TreeDataTypeSvg.vue';
import Node from "element-plus/lib/components/tree/src/model/node.js";
import ContextMenuVue from '../../components/ContextMenu.vue';
import { ElMessage, ElNotification } from 'element-plus';
import helper from '../../common/helpler';
import { TreeInstance } from 'element-plus/lib/components/index.js';
import { smlService } from '../../common/api';

const props = defineProps<{ connectionStatus: ConnectionStatus, globalId: number, }>();
const emits = defineEmits(["sendPrimaryMessage", "save"]);

const data = reactive<{
    treeData: MessageTreeData[],
}>({
    treeData: [],
})

const treeTransactions = shallowRef<TreeInstance>();
const messageDialogApi = inject<DialogApi<Message>>('messagePropertyDialogApi');
const valuePropertyDialogApi = inject<DialogApi<MessageValueToEdit>>('valuePropertyDialogApi');
const receiveActionDialogApi = inject<DialogApi<ReceiveAction>>('receiveActionDialogApi');
const setCopiedData = inject<(data: MessageTreeData | null, isCut: boolean) => void>("setCopiedData");
const getCopiedData = inject<() => CopiedData>("getCopiedData");

const smlLoadImpl = inject<(isRefresh: boolean) => {}>("smlLoad");
// 当前选中的数据
let currentTreeNode: Node | null = null;
const contextMenuRef = shallowRef();
const openRecieveAction = (): boolean | void => {
    if (currentTreeNode == null || receiveActionDialogApi == null) {
        return;
    }
    receiveActionDialogApi.data.value.matchRules = currentTreeNode.data.tag.receiveAction.matchRules;
    receiveActionDialogApi.data.value.perforAction = currentTreeNode.data.tag.receiveAction.perforAction;
    receiveActionDialogApi.data.value.replySecondary = currentTreeNode.data.tag.receiveAction.replySecondary;
    receiveActionDialogApi.data.value.transactionNames = data.treeData.map(x => { return x.name + ' ' + x.desc });
    receiveActionDialogApi.data.value.namesInTransaction = getItemNames(currentTreeNode.data) ?? [];
    receiveActionDialogApi.open("Receive action property").then((result) => {
        if (result != DialogResult.Ok || currentTreeNode == null) {
            return;
        }
        currentTreeNode.data.tag.receiveAction.replySecondary = receiveActionDialogApi.data.value.replySecondary;
        currentTreeNode.data.tag.receiveAction.matchRules = receiveActionDialogApi.data.value.matchRules;
        currentTreeNode.data.tag.receiveAction.perforAction = receiveActionDialogApi.data.value.perforAction;
        syncTransaction(currentTreeNode, SyncTransactionAction.Update);
    });
}

const getItemNames = (node: any): string[] | null => {
    let names: string[] = [];
    if (node.tag.type == MessageTreeDataType.Secondary) {
        return null;
    }

    names.push(node.name);
    if (node.children) {
        node.children.forEach((item: any) => {
            let subNames = getItemNames(item);
            if (subNames) {
                subNames.forEach((sub: string) => {
                    if (!names.includes(sub) && sub.trim() !== '') {
                        names.push(sub);
                    }
                });
            }
        });
    }
    return names;
}

const openProperty = (): boolean | void => {
    if (currentTreeNode == null || messageDialogApi == null || valuePropertyDialogApi == null) {
        return;
    }
    if (currentTreeNode.data.tag.type == MessageTreeDataType.Transaction) {
        messageDialogApi.data.value.name = currentTreeNode.data.name;
        messageDialogApi.data.value.desc = currentTreeNode.data.desc;
        messageDialogApi.data.value.body = null;
        messageDialogApi.open("Transaction Property").then((result) => {
            if (result != DialogResult.Ok || currentTreeNode == null) {
                return;
            }
            currentTreeNode.data.name = messageDialogApi.data.value.name;
            currentTreeNode.data.desc = messageDialogApi.data.value.desc;
            syncTransaction(currentTreeNode, SyncTransactionAction.Update);
        });
    }
    else if (currentTreeNode?.data.tag.type == MessageTreeDataType.Primary || currentTreeNode?.data.tag.type == MessageTreeDataType.Secondary) {
        messageDialogApi.data.value.name = currentTreeNode.data.name;
        messageDialogApi.data.value.desc = currentTreeNode.data.desc;
        messageDialogApi.data.value.head.s = currentTreeNode.data.tag.s;
        messageDialogApi.data.value.head.f = currentTreeNode.data.tag.f;
        messageDialogApi.data.value.head.isExpectReply = currentTreeNode.data.tag.isReply;
        messageDialogApi.data.value.body = {} as MessageValue;
        messageDialogApi.open(`S${messageDialogApi.data.value.head.s}F${messageDialogApi.data.value.head.f} Property`).then((result) => {
            if (result != DialogResult.Ok || currentTreeNode == null) {
                return;
            }
            currentTreeNode.data.name = messageDialogApi.data.value.name;
            currentTreeNode.data.desc = messageDialogApi.data.value.desc;
            currentTreeNode.data.tag.s = messageDialogApi.data.value.head.s;
            currentTreeNode.data.tag.f = messageDialogApi.data.value.head.f;
            currentTreeNode.data.tag.isReply = messageDialogApi.data.value.head.isExpectReply;
            syncTransaction(currentTreeNode, SyncTransactionAction.Update);
        });
    }
    else {
        valuePropertyDialogApi.data.value.name = currentTreeNode.data.name;
        valuePropertyDialogApi.data.value.desc = currentTreeNode.data.desc;
        valuePropertyDialogApi.data.value.format = currentTreeNode.data.tag.format;
        valuePropertyDialogApi.data.value.valueString = currentTreeNode.data.valueString;
        valuePropertyDialogApi.data.value.value = null;
        // let valueString = null as any;
        // valuePropertyDialogApi.open(`${valuePropertyDialogApi.data.value.name || "Value"} Property`, () => {
        //     valueString = valuePropertyDialogApi.data.value.valueString;
        //     return false;
        // })
        valuePropertyDialogApi.open(`${valuePropertyDialogApi.data.value.name || "Value"} Property`).then((result) => {
            if (result != DialogResult.Ok || currentTreeNode == null) {
                return;
            }
            currentTreeNode.data.name = valuePropertyDialogApi.data.value.name;
            currentTreeNode.data.tag.format = valuePropertyDialogApi.data.value.format;
            currentTreeNode.data.desc = valuePropertyDialogApi.data.value.desc;
            currentTreeNode.data.tag.value = valuePropertyDialogApi.data.value.value;
            currentTreeNode.data.valueString = helper.parseValueToStringWithoutCheck(valuePropertyDialogApi.data.value.value, valuePropertyDialogApi.data.value.format);
            if (currentTreeNode.data.tag.format != SecsFormat.List && currentTreeNode.data.children != null && currentTreeNode.data.children.length > 0) {
                for (let i = currentTreeNode.data.children.length; i > 0; i--) {
                    treeTransactions.value?.remove(currentTreeNode.data.children[i - 1]);
                }
                currentTreeNode.data.children = [];
            }
            syncTransaction(currentTreeNode, SyncTransactionAction.Update);
        })
    }
    contextMenuRef.value.hide();
    return true;
}


const syncTransaction = (treeNode: Node, action: SyncTransactionAction) => {
    while (treeNode != null && treeNode.data.tag.type != MessageTreeDataType.Transaction) {
        treeNode = treeNode.parent;
    }
    if (treeNode == null) {
        return;
    }
    let transaction = helper.parseToTransaction(treeNode.data as MessageTreeData);
    transaction.primary.head.systemByte = 0;
    if (transaction.secondary != null) {
        transaction.secondary.head.systemByte = 0;
    }
    smlService.syncTransaction(props.globalId, treeNode.data.id, transaction, action).then(_ => { }).catch(err => {
        ElMessage.error({
            plain: true,
            message: err.toString(),
        })
    })
}

const isAllowDuplicatie = (): boolean => {
    return currentTreeNode?.data.tag.type == MessageTreeDataType.Transaction || currentTreeNode?.data.tag.type == MessageTreeDataType.Value;
}

const duplicate = () => {
    if (currentTreeNode == null || !isAllowDuplicatie()) {
        return;
    }
    const dupData = helper.duplicateTreeData(currentTreeNode.data as MessageTreeData, true);
    if (dupData == null) {
        return;
    }
    if (currentTreeNode.data.tag.type == MessageTreeDataType.Transaction) {
        let transaction = helper.parseToTransaction(dupData as MessageTreeData);
        transaction.primary.head.systemByte = 0;
        if (transaction.secondary != null) {
            transaction.secondary.head.systemByte = 0;
        }
        smlService.syncTransaction(props.globalId, currentTreeNode.data.id, transaction, SyncTransactionAction.InsertAfter).then(res => {
            if (currentTreeNode == null) {
                return;
            }
            dupData.id = res;
            treeTransactions.value?.insertAfter(dupData, currentTreeNode);
            nextTick(() => {
                setCheckedNode(dupData.id);
            });
        }).catch(err => {
            ElMessage.error({
                plain: true,
                message: err.toString(),
            })
        })
    }
    else {
        treeTransactions.value?.insertAfter(dupData, currentTreeNode);
        syncTransaction(currentTreeNode, SyncTransactionAction.Update);
        nextTick(() => {
            setCheckedNode(dupData.id);
        });
    }

    contextMenuRef.value.hide();
}

const isEnableSendPrimaryMessage = (): boolean => {
    if (!currentTreeNode) {
        return false;
    }
    return props.connectionStatus == ConnectionStatus.Selected && (currentTreeNode.data.tag.type == MessageTreeDataType.Transaction || currentTreeNode.data.tag.type == MessageTreeDataType.Primary)
}

const sendPrimaryMessage = () => {
    if (!currentTreeNode || !isEnableSendPrimaryMessage()) {
        return;
    }
    if (currentTreeNode.data.tag.type == MessageTreeDataType.Transaction) {
        // emits("sendPrimaryMessage", helper.parseToTransactionData(currentTreeNode.data as MessageTreeData));
        emits("sendPrimaryMessage", currentTreeNode.data.id);
    }
    else if (currentTreeNode.data.tag.type == MessageTreeDataType.Primary) {
        // emits("sendPrimaryMessage", helper.parseToTransactionData(currentTreeNode.parent.data as MessageTreeData));
        emits("sendPrimaryMessage", currentTreeNode.parent.data.id);
    }
    contextMenuRef.value.hide();
    return true;
}

const isEnableInsertPreviousItem = (): boolean => {
    return currentTreeNode?.data.tag.type == MessageTreeDataType.Value;
}

const insertPreviousItem = () => {
    if (currentTreeNode == null || !isEnableInsertPreviousItem()) {
        return false;
    }
    let itemData = helper.newEmptyValueTreeItem();
    treeTransactions.value?.insertBefore(itemData, currentTreeNode);
    nextTick(() => {
        setCheckedNode(itemData.id);
    });
    syncTransaction(currentTreeNode, SyncTransactionAction.Update);
    contextMenuRef.value.hide();
}

const isEnableInsertNextItem = (): boolean => {
    return currentTreeNode?.data.tag.type == MessageTreeDataType.Value;
}

const insertNextItem = () => {
    if (currentTreeNode == null || !isEnableInsertNextItem()) {
        return false;
    }
    let itemData = helper.newEmptyValueTreeItem();
    treeTransactions.value?.insertAfter(itemData, currentTreeNode);
    nextTick(() => {
        setCheckedNode(itemData.id);
    });
    syncTransaction(currentTreeNode, SyncTransactionAction.Update);
    contextMenuRef.value.hide();
}

const isEnableInsertChildItem = (): boolean => {
    if (currentTreeNode == null) {
        return false;
    }
    if (currentTreeNode.data.tag.type == MessageTreeDataType.Primary || currentTreeNode.data.tag.type == MessageTreeDataType.Secondary) {
        return currentTreeNode.data.children == undefined || currentTreeNode.data.children.length == 0;
    } else {
        return currentTreeNode.data.tag.format == SecsFormat.List;
    }
}

const insertChildItem = () => {
    if (currentTreeNode == null || !isEnableInsertChildItem()) {
        return false;
    }
    let itemData = helper.newEmptyValueTreeItem();
    if (currentTreeNode.data.tag.type == MessageTreeDataType.Primary || currentTreeNode.data.tag.type == MessageTreeDataType.Secondary) {
        itemData.tag.type = MessageTreeDataType.RootValue;
    }
    treeTransactions.value?.append(itemData, currentTreeNode);
    nextTick(() => {
        setCheckedNode(itemData.id);
    });
    syncTransaction(currentTreeNode, SyncTransactionAction.Update);
    contextMenuRef.value.hide();
}

const setCheckedNode = (nodeId: string) => {
    treeTransactions.value?.setCurrentKey(nodeId, true);
    let targetNode = treeTransactions.value?.getNode(nodeId);
    if (targetNode) {
        currentTreeNode = targetNode;
    }
}


const isShowCopy = () => {
    if (currentTreeNode?.data.tag.type == MessageTreeDataType.RootValue || currentTreeNode?.data.tag.type == MessageTreeDataType.Value) {
        return true;
    }
    return false;
}

const copy = () => {
    if (!isShowCopy()) {
        return false;
    }
    setCopiedData && setCopiedData(helper.duplicateTreeData(currentTreeNode?.data as MessageTreeData, false), false);
    contextMenuRef.value.hide();
}

const isShowPasteToPreviousOrNext = () => {
    return currentTreeNode?.data.tag.type == MessageTreeDataType.Value;
}

const isEnablePaste = () => {
    return (getCopiedData && getCopiedData()) != null;
}

const pasteToPrevious = () => {
    const copiedData = getCopiedData && getCopiedData();
    if (currentTreeNode == null || !isShowPasteToPreviousOrNext() || !isEnablePaste() || copiedData == null || copiedData.value == null) {
        return false;
    }
    copiedData.value.tag.type = MessageTreeDataType.Value;
    let data = helper.duplicateTreeData(copiedData.value, true);
    if (data != null) {
        treeTransactions.value?.insertBefore(data, currentTreeNode);
        nextTick(() => {
            setCheckedNode(data.id);
        });
    }
    syncTransaction(currentTreeNode, SyncTransactionAction.Update);
    contextMenuRef.value.hide();
}

const pasteToNext = () => {
    const copiedData = getCopiedData && getCopiedData();
    if (currentTreeNode == null || !isShowPasteToPreviousOrNext() || !isEnablePaste() || copiedData == null || copiedData.value == null) {
        return false;
    }
    copiedData.value.tag.type = MessageTreeDataType.Value;
    let data = helper.duplicateTreeData(copiedData.value, true);
    if (data != null) {
        treeTransactions.value?.insertAfter(data, currentTreeNode);
        nextTick(() => {
            setCheckedNode(data.id);
        });
    }
    syncTransaction(currentTreeNode, SyncTransactionAction.Update);
    contextMenuRef.value.hide();
}

const isShowPasteToChild = () => {
    return currentTreeNode?.data.tag.format == SecsFormat.List || ((currentTreeNode?.data.tag.type == MessageTreeDataType.Primary || currentTreeNode?.data.tag.type == MessageTreeDataType.Secondary) && (currentTreeNode?.data.children == null || currentTreeNode?.data.children.length == 0));
}

const pasteToChild = () => {
    const copiedData = getCopiedData && getCopiedData();
    if (currentTreeNode == null || !isShowPasteToChild() || !isEnablePaste() || copiedData == null || copiedData.value == null) {
        return false;
    }
    if (currentTreeNode.data.tag.type == MessageTreeDataType.Primary || currentTreeNode.data.tag.type == MessageTreeDataType.Secondary) {
        copiedData.value.tag.type = MessageTreeDataType.RootValue;
    }
    else {
        copiedData.value.tag.type = MessageTreeDataType.Value;
    }
    let data = helper.duplicateTreeData(copiedData.value, true);
    if (data != null) {
        treeTransactions.value?.append(data, currentTreeNode!);
        nextTick(() => {
            setCheckedNode(data.id);
        });
    }
    syncTransaction(currentTreeNode, SyncTransactionAction.Update);
    contextMenuRef.value.hide();
}

const isShowReplace = () => {
    if (!currentTreeNode) {
        return false;
    }
    return currentTreeNode.data.tag.type == MessageTreeDataType.RootValue || currentTreeNode.data.tag.type == MessageTreeDataType.Value;
}

const isEnableReplace = () => {
    return (getCopiedData && getCopiedData()) != null;
}

const replace = () => {
    const copiedData = getCopiedData && getCopiedData();
    if (currentTreeNode == null || copiedData == null || copiedData.value == null || !isShowReplace() || !isEnableReplace()) {
        return;
    }
    let data = helper.duplicateTreeData(copiedData.value, true);
    if (data != null) {
        const parentNode = currentTreeNode.parent;
        treeTransactions.value?.insertAfter(data, currentTreeNode);
        treeTransactions.value?.remove(currentTreeNode);
        nextTick(() => {
            setCheckedNode(data.id);
        });
        // 删除当前节点
        syncTransaction(parentNode, SyncTransactionAction.Update);
    }
}

const refresh = () => {
    if (smlLoadImpl != null) {
        smlLoadImpl(true);
    }
    contextMenuRef.value.hide();
}

const reload = () => {
    if (smlLoadImpl != null) {
        smlLoadImpl(false);
    }
    contextMenuRef.value.hide();
}

const isShowDelete = () => {
    return currentTreeNode?.data.tag.type == MessageTreeDataType.Transaction || currentTreeNode?.data.tag.type == MessageTreeDataType.RootValue || currentTreeNode?.data.tag.type == MessageTreeDataType.Value;
}

const deleteNode = () => {
    if (currentTreeNode == null || !isShowDelete()) {
        return false;
    }
    // 默认选中下一个
    let next = currentTreeNode.nextSibling;
    if (next == null) {
        next = currentTreeNode.previousSibling;
    }
    if (currentTreeNode.data.tag.type == MessageTreeDataType.Transaction) {
        if (data.treeData.length == 1) {
            return;
        }
        treeTransactions.value?.remove(currentTreeNode);
        smlService.delTransaction(props.globalId, currentTreeNode.data.id).then(_ => {
        }).catch(err => {
            ElMessage.error({
                plain: true,
                message: err.toString(),
            })
        })
    }
    else if (currentTreeNode.data.tag.type == MessageTreeDataType.RootValue || currentTreeNode.data.tag.type == MessageTreeDataType.Value) {
        const parentNode = currentTreeNode.parent;
        treeTransactions.value?.remove(currentTreeNode);
        syncTransaction(parentNode, SyncTransactionAction.Update);
    }
    if (next == null) {
        currentTreeNode = null;
    }
    else {
        treeTransactions.value?.setCurrentKey(next.key, false);
        currentTreeNode = next;
    }
    contextMenuRef.value.hide();
    return true;
}

const isShowExpand = () => {
    return currentTreeNode?.isLeaf == false;
}

const expand = () => {
    if (currentTreeNode == null || !isShowExpand()) {
        return;
    }
    helper.expandNode(currentTreeNode, true);
    return true;
}

const isShowCollapse = () => {
    return currentTreeNode?.isLeaf == false;
}

const isEnableCollapse = () => {
    return currentTreeNode?.expanded == true;
}

const collapse = () => {
    if (currentTreeNode == null || !isShowCollapse() || !isEnableCollapse()) {
        return;
    }
    currentTreeNode.collapse();
    return true;
}

const contextMenuItems: TreeMenuItem[] = [
    {
        label: 'Property',
        keyboard: "F1",
        clickHandler: openProperty,
    }, {
        label: 'Send Primary Message',
        keyboard: "F2",
        isEnable: isEnableSendPrimaryMessage,
        clickHandler: sendPrimaryMessage,
    }, {
        label: 'Receive action',
        isEnable: function (): boolean {
            return currentTreeNode?.data.tag.type == MessageTreeDataType.Transaction || currentTreeNode?.data.tag.type == MessageTreeDataType.Primary
        },
        clickHandler: openRecieveAction,
    },
    {
        label: "-",
    }, {
        label: 'Expand',
        keyboard: "-",
        isVisible: isShowExpand,
        clickHandler: expand,
    }, {
        label: 'Collapse',
        keyboard: "+",
        icon: '',
        isVisible: isShowCollapse,
        isEnable: isEnableCollapse,
        clickHandler: collapse,
    },
    {
        label: "Insert Previous Item",
        keyboard: "F3",
        isEnable: isEnableInsertPreviousItem,
        clickHandler: insertPreviousItem
    },
    {
        label: "Insert Next Item",
        keyboard: "F4",
        isEnable: isEnableInsertNextItem,
        clickHandler: insertNextItem
    },
    {
        label: "Insert Child Item",
        keyboard: "Ctrl + Q",
        isEnable: isEnableInsertChildItem,
        clickHandler: insertChildItem
    },
    {
        label: "-",
    },
    {
        label: "Copy",
        keyboard: "Ctrl + C",
        isVisible: isShowCopy,
        clickHandler: copy
    },
    {
        label: "Paste To Previous",
        keyboard: "Ctrl + B",
        isVisible: isShowPasteToPreviousOrNext,
        isEnable: isEnablePaste,
        clickHandler: pasteToPrevious
    },
    {
        label: "Paste To Next",
        keyboard: "Ctrl + N",
        isVisible: isShowPasteToPreviousOrNext,
        isEnable: isEnablePaste,
        clickHandler: pasteToNext
    },
    {
        label: "Paste As Child",
        keyboard: "Ctrl + V",
        isVisible: isShowPasteToChild,
        isEnable: isEnablePaste,
        clickHandler: pasteToChild
    },
    {
        label: "Replace",
        keyboard: "Ctrl + L",
        isVisible: isShowReplace,
        isEnable: isEnableReplace,
        clickHandler: replace
    },
    {
        label: "Duplicate",
        keyboard: "Ctrl + D",
        isEnable: isAllowDuplicatie,
        clickHandler: duplicate
    },
    {
        label: "-",
    },
    {
        label: "Refresh",
        keyboard: "Ctrl + R",
        clickHandler: refresh
    },
    {
        label: "Reload",
        keyboard: "Ctrl + E",
        clickHandler: reload
    },
    {
        label: 'Delete',
        keyboard: "Del",
        isVisible: isShowDelete,
        clickHandler: deleteNode,
    }
]



const handleNodeClick = (_data: any, node: Node) => {
    currentTreeNode = node;
    contextMenuRef.value.hide();
}

const defaultProps = {
    children: 'children',
    label: 'label'
}

const load = (messages: MessageTransaction[]) => {
    let treeData: MessageTreeData[] = [];
    try {
        for (var i = 0; i < messages.length; i++) {
            treeData.push(helper.parseToMessageTreeDataFull(messages[i]));
        }
    }
    catch (err) {
        ElNotification.error(err as any);
        console.error(err);
    }
    data.treeData = treeData;
};

const handleContextMenu = (evt: PointerEvent, _data: MessageTreeData, node: Node) => {
    currentTreeNode = node;
    contextMenuRef.value.show(evt.clientX, evt.clientY);
}


/// @messageData: primary mesage
const findMatchedTransaction = (messageData: MessageTreeData): MessageTransactionData | null => {
    let isIgnoreValue = true;
    if (messageData.tag.type == MessageTreeDataType.Primary || messageData.tag.type == MessageTreeDataType.Secondary) {
        if (messageData.tag.s == 1 && messageData.tag.f == 3) {
            isIgnoreValue = false;
        }
    }
    return helper.findMatchedTransaction(data.treeData, messageData, false, isIgnoreValue);
}

const addTransaction = () => {
    if (!messageDialogApi) {
        return;
    }
    messageDialogApi.data.value.name = '';
    messageDialogApi.data.value.desc = "new transaction";
    messageDialogApi.data.value.body = {} as MessageValue;
    messageDialogApi.data.value.head.s = 1;
    messageDialogApi.data.value.head.f = 1;
    messageDialogApi.open("Transaction Property").then((result) => {
        if (result != DialogResult.Ok) {
            return;
        }
        let primaryMessage: MessageTreeData = {
            id: (++helper.globalId).toString(),
            name: 'S' + messageDialogApi.data.value.head.s + 'F' + messageDialogApi.data.value.head.f,
            desc: '',
            tag: {
                s: messageDialogApi.data.value.head.s,
                f: messageDialogApi.data.value.head.f,
                isReply: messageDialogApi.data.value.head.isExpectReply,
                type: MessageTreeDataType.Primary,
                systemByte: 0,
            } as MessageTag,
            children: [],
            valueString: null,
        }

        let secondaryMessage: MessageTreeData = {
            id: (++helper.globalId).toString(),
            name: 'S' + messageDialogApi.data.value.head.s + 'F' + (messageDialogApi.data.value.head.f + 1),
            desc: '',
            tag: {
                type: MessageTreeDataType.Secondary,
                s: messageDialogApi.data.value.head.s,
                f: messageDialogApi.data.value.head.f + 1,
                isReply: false,
                systemByte: 0,
            } as MessageTag,
            children: [],
            valueString: null,
        }

        let item: MessageTreeData = {
            id: (++helper.globalId).toString(),
            name: messageDialogApi.data.value.name.length === 0 ? primaryMessage.name : messageDialogApi.data.value.name,
            desc: messageDialogApi.data.value.desc,
            children: [],
            tag: {
                type: MessageTreeDataType.Transaction, receiveAction: {
                    matchRules: [],
                    replySecondary: true,
                    perforAction: []
                }
            },
            valueString: null,
        };

        item.children?.push
        item.children!.push(primaryMessage);
        item.children!.push(secondaryMessage);
        const transaction = helper.parseToTransaction(item);
        smlService.syncTransaction(props.globalId, "", transaction, SyncTransactionAction.Append).then(res => {
            item.id = res;
            data.treeData.push(item);
        }).catch(err => {
            ElMessage.error({
                plain: true,
                message: err.toString(),
            })
        });
    });
}

const moveUp = () => {
    if (currentTreeNode == null || !currentTreeNode.previousSibling || (currentTreeNode.data.tag.type != MessageTreeDataType.Transaction && currentTreeNode.data.tag.type != MessageTreeDataType.Value)) {
        return;
    }
    let targetData = helper.duplicateTreeData(currentTreeNode.data as MessageTreeData, false);
    const depNode = currentTreeNode.previousSibling;
    treeTransactions.value?.remove(currentTreeNode);
    currentTreeNode = null;
    if (targetData == null) {
        return;
    }
    smlService.swapTransaction(props.globalId, targetData.id, depNode.data.id).then(_ => {
        treeTransactions.value?.insertBefore(targetData, depNode);
        nextTick(() => {
            treeTransactions.value?.setCurrentKey(targetData.id);
            let targetNode = treeTransactions.value?.getNode(targetData.id);
            if (targetNode) {
                currentTreeNode = targetNode;
            }
        })
    }).catch(err => {
        ElMessage.error({
            plain: true,
            message: err.toString(),
        })
    });
}

const moveDown = () => {
    if (currentTreeNode == null || !currentTreeNode.nextSibling || (currentTreeNode.data.tag.type != MessageTreeDataType.Transaction && currentTreeNode.data.tag.type != MessageTreeDataType.Value)) {
        return;
    }
    let targetData = helper.duplicateTreeData(currentTreeNode.data as MessageTreeData, false);
    const depNode = currentTreeNode.nextSibling;
    treeTransactions.value?.remove(currentTreeNode);
    currentTreeNode = null;
    if (targetData == null) {
        return;
    }
    smlService.swapTransaction(props.globalId, targetData.id, depNode.data.id).then(_ => {
        treeTransactions.value?.insertAfter(targetData, depNode);
        nextTick(() => {
            setCheckedNode(targetData.id);
        })
    }).catch(err => {
        ElMessage.error({
            plain: true,
            message: err.toString(),
        })
    });

}

const onTransactionAreaContextMenu = (evt: MouseEvent) => {
    evt.preventDefault();
}

const handleNodeDblClick = (treeDataId: string) => {
    let targetNode = treeTransactions.value?.getNode(treeDataId);
    if (targetNode == null) {
        return;
    }
    if (targetNode.data.tag.type == MessageTreeDataType.RootValue || targetNode.data.tag.type == MessageTreeDataType.Value) {
        if (targetNode.data.tag.format != SecsFormat.List) {
            currentTreeNode = targetNode;
            openProperty()
            return;
        }
    }
    if (targetNode.expanded) {
        targetNode.collapse();
    }
    else {
        targetNode.expand();
    }
}

const save = (evt: MouseEvent) => {
    emits("save", evt.altKey);
}

const openLogDir = () => {
    smlService.openLogDir(props.globalId);
}

const newTransaction = (transaction: MessageTransaction) => {
    let treeData = helper.parseToMessageTreeDataFull(transaction);
    smlService.syncTransaction(props.globalId, "", transaction, SyncTransactionAction.Append).then(res => {
        treeData.id = res;
        data.treeData.push(treeData);
    }).catch(err => {
        ElMessage.error({
            plain: true,
            message: err.toString(),
        })
    });
}

defineExpose({ load, findMatchedTransaction, openProperty, duplicate, sendPrimaryMessage, insertPreviousItem, insertNextItem, insertChildItem, copy, pasteToPrevious, pasteToNext, pasteToChild, refresh, reload, deleteNode, expand, collapse, newTransaction, replace });
</script>
<template>
    <div class="app-toolbar-2" style="flex-grow: 0;">
        <div class="lib-op-i">
            <el-icon title="Add New Transaction" @click="addTransaction">
                <Plus />
            </el-icon>
            <el-icon title="Duplicate" @click="duplicate">
                <CopyDocument />
            </el-icon>
            <el-icon title="Move Up" @click="moveUp">
                <Top />
            </el-icon>
            <el-icon title="Move Down" @click="moveDown">
                <Bottom />
            </el-icon>
            <el-icon title="Save     Ctrl + S&#13;SaveAs Alt + S" @click="save($event)">
                <CircleCheck />
            </el-icon>
            <el-icon title="Open Log Dir" @click="openLogDir">
                <Memo />
            </el-icon>
        </div>
    </div>
    <div style="flex-grow: 1;overflow: auto;" @contextmenu="onTransactionAreaContextMenu($event)">
        <el-scrollbar>
            <el-tree ref="treeTransactions" class="lib-tree" style="max-width: 600px" :data="data.treeData" :indent="10"
                :props="defaultProps" @node-click="handleNodeClick" @node-contextmenu="handleContextMenu"
                :highlight-current="true" node-key="id" :expand-on-click-node="false">
                <template #default="{ data }">
                    <div @dblclick="handleNodeDblClick(data.id)"
                        style="user-select: none;display: flex;align-items: center;">
                        <TreeDataTypeSvgVue :type="data.tag.type" :format="data.tag.format"
                            :length="data.children == null ? 0 : data.children.length"></TreeDataTypeSvgVue>
                        <span style="margin-left:3px;">{{ data.name }}</span>
                        <span v-if="data.valueString != null && data.valueString != ''" style="margin-left: 4px;"> = {{
                            data.valueString }}</span>
                        <span v-if="data.desc != null && data.desc.length > 0"
                            style="margin-left:4px; display: inline;color:#555555"> {{
                                data.desc
                            }}</span>
                    </div>
                </template>
            </el-tree>
        </el-scrollbar>
    </div>
    <ContextMenuVue ref="contextMenuRef" :items="contextMenuItems"></ContextMenuVue>
</template>